/**

 @Title: layui.upload 文件上传
 @Author: 贤心
 @License：MIT

 */

layui.define('layer', function (exports) {
  "use strict";

  var $ = layui.$
    , layer = layui.layer
    , hint = layui.hint()
    , device = layui.device()

    //外部接口
    , upload = {
      config: {} //全局配置项

      //设置全局项
      , set: function (options) {
        var that = this;
        that.config = $.extend({}, that.config, options);
        return that;
      }

      //事件监听
      , on: function (events, callback) {
        return layui.onevent.call(this, MOD_NAME, events, callback);
      }
    }

    //操作当前实例
    , thisUpload = function () {
      var that = this;
      return {
        upload: function (files) {
          that.upload.call(that, files);
        }
        , config: that.config
      }
    }

    //字符常量
    , MOD_NAME = 'upload', ELEM = '.layui-upload', THIS = 'layui-this', SHOW = 'layui-show', HIDE = 'layui-hide', DISABLED = 'layui-disabled'

    , ELEM_FILE = 'layui-upload-file', ELEM_FORM = 'layui-upload-form', ELEM_IFRAME = 'layui-upload-iframe', ELEM_CHOOSE = 'layui-upload-choose', ELEM_DRAG = 'layui-upload-drag'


    //构造器
    , Class = function (options) {
      var that = this;
      that.config = $.extend({}, that.config, upload.config, options);
      that.render();
    };

  //默认配置
  Class.prototype.config = {
    accept: 'images' //允许上传的文件类型：images/file/video/audio
    , exts: '' //允许上传的文件后缀名
    , auto: true //是否选完文件后自动上传
    , bindAction: '' //手动上传触发的元素
    , url: '' //上传地址
    , field: 'file' //文件字段名
    , method: 'post' //请求上传的http类型
    , data: {} //请求上传的额外参数
    , drag: true //是否允许拖拽上传
    , size: 0 //文件限制大小，默认不限制
    , multiple: false //是否允许多文件上传，不支持ie8-9
  };

  //初始渲染
  Class.prototype.render = function (options) {
    var that = this
      , options = that.config;

    options.elem = $(options.elem);
    options.bindAction = $(options.bindAction);

    that.file();
    that.events();
  };

  //追加文件域
  Class.prototype.file = function () {
    var that = this
      , options = that.config
      , elemFile = that.elemFile = $([
        '<input class="' + ELEM_FILE + '" type="file" name="' + options.field + '"'
        , (options.multiple ? ' multiple' : '')
        , '>'
      ].join(''))
      , next = options.elem.next();

    if (next.hasClass(ELEM_FILE) || next.hasClass(ELEM_FORM)) {
      next.remove();
    }

    //包裹ie8/9容器
    if (device.ie && device.ie < 10) {
      options.elem.wrap('<div class="layui-upload-wrap"></div>');
    }

    that.isFile() ? (
      that.elemFile = options.elem
      , options.field = options.elem[0].name
    ) : options.elem.after(elemFile);

    //初始化ie8/9的Form域
    if (device.ie && device.ie < 10) {
      that.initIE();
    }
  };

  //ie8-9初始化
  Class.prototype.initIE = function () {
    var that = this
      , options = that.config
      , iframe = $('<iframe id="' + ELEM_IFRAME + '" class="' + ELEM_IFRAME + '" name="' + ELEM_IFRAME + '" frameborder="0"></iframe>')
      , elemForm = $(['<form target="' + ELEM_IFRAME + '" class="' + ELEM_FORM + '" method="' + options.method
        , '" key="set-mine" enctype="multipart/form-data" action="' + options.url + '">'
        , '</form>'].join(''));

    //插入iframe    
    $('#' + ELEM_IFRAME)[0] || $('body').append(iframe);

    //包裹文件域
    if (!options.elem.next().hasClass(ELEM_IFRAME)) {
      that.elemFile.wrap(elemForm);

      //追加额外的参数
      options.elem.next('.' + ELEM_IFRAME).append(function () {
        var arr = [];
        layui.each(options.data, function (key, value) {
          arr.push('<input type="hidden" name="' + key + '" value="' + value + '">')
        });
        return arr.join('');
      }());
    }
  };

  //异常提示
  Class.prototype.msg = function (content) {
    return layer.msg(content, {
      icon: 2
      , shift: 6
    });
  };

  //判断绑定元素是否为文件域本身
  Class.prototype.isFile = function () {
    var elem = this.config.elem[0];
    if (!elem) return;
    return elem.tagName.toLocaleLowerCase() === 'input' && elem.type === 'file'
  }

  //预读图片信息
  Class.prototype.preview = function (callback) {
    var that = this;
    if (window.FileReader) {
      layui.each(that.chooseFiles, function (index, file) {
        var reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = function () {
          callback && callback(index, file, this.result);
        }
      });
    }
  };

  //执行上传
  Class.prototype.upload = function (files, type) {
    var that = this
      , options = that.config
      , elemFile = that.elemFile[0]

      //高级浏览器处理方式，支持跨域
      , ajaxSend = function () {
        layui.each(files || that.files || that.chooseFiles || elemFile.files, function (index, file) {
          var formData = new FormData();

          formData.append(options.field, file);

          //追加额外的参数
          layui.each(options.data, function (key, value) {
            formData.append(key, value);
          });
        
          $.ajax({
            url: options.url
            , type: options.method
            , data: formData
            , contentType: false
            , processData: false
            , dataType: 'json'
            , success: function (res) {
              done(index, res);
            }
            , error: function () {
              that.msg('请求上传接口出现异常');
              error(index);
            }
          });
        });
      }

      //低版本IE处理方式，不支持跨域
      , iframeSend = function () {
        var iframe = $('#' + ELEM_IFRAME);

        that.elemFile.parent().submit();

        //获取响应信息
        clearInterval(Class.timer);
        Class.timer = setInterval(function () {
          var res, iframeBody = iframe.contents().find('body');
          try {
            res = iframeBody.text();
          } catch (e) {
            that.msg('获取上传后的响应信息出现异常');
            clearInterval(Class.timer);
            error();
          }
          if (res) {
            clearInterval(Class.timer);
            iframeBody.html('');
            done(0, res);
          }
        }, 30);
      }

      //统一回调
      , done = function (index, res) {
        that.elemFile.next('.' + ELEM_CHOOSE).remove();
        elemFile.value = '';
        if (typeof res !== 'object') {
          try {
            res = JSON.parse(res);
          } catch (e) {
            res = {};
            return that.msg('请对上传接口返回有效JSON');
          }
        }
        typeof options.done === 'function' && options.done(res, index || 0, function (files) {
          that.upload(files);
        });
      }

      //统一网络异常回调
      , error = function (index) {
        if (options.auto) {
          elemFile.value = '';
        }
        typeof options.error === 'function' && options.error(index || 0, function (files) {
          that.upload(files);
        });
      }

      , exts = options.exts
      , check, value = function () {
        var arr = [];
        layui.each(files || that.chooseFiles, function (i, item) {
          arr.push(item.name);
        });
        return arr;
      }()

      //回调返回的参数
      , args = {
        preview: function (callback) {
          that.preview(callback);
        }
        , upload: function (index, file) {
          var thisFile = {};
          thisFile[index] = file;
          that.upload(thisFile);
        }
        , pushFile: function () {
          that.files = that.files || {};
          layui.each(that.chooseFiles, function (index, item) {
            that.files[index] = item;
          });
          return that.files;
        }
        , options: options // options 通过这个参数可以修改内部配置
      }

      //提交上传
      , send = function () {
        if (type === 'choose') {
          return options.choose && options.choose(args);
        }

        //上传前的回调
        var canUpload = true;
        var __url = null;
        if (options.before) {
          canUpload = options.before(args); //add by sunny & liyj
        }

        if (canUpload) {
          //IE兼容处理
          if (device.ie) {
            return device.ie > 9 ? ajaxSend() : iframeSend();
          }

          ajaxSend();
        }
      }

    //校验文件格式
    value = value.length === 0
      ? ((elemFile.value.match(/[^\/\\]+\..+/g) || []) || '') : value;

    switch (options.accept) {
      case 'file': //一般文件
        if (exts && !RegExp('\\w\\.(' + exts + ')$', 'i').test(escape(value))) {
          that.msg('选择的文件中包含不支持的格式');
          return elemFile.value = '';
        }
        break;
      case 'video': //视频文件
        if (!RegExp('\\w\\.(' + (exts || 'avi|mp4|wma|rmvb|rm|flash|3gp|flv') + ')$', 'i').test(escape(value))) {
          that.msg('选择的视频中包含不支持的格式');
          return elemFile.value = '';
        }
        break;
      case 'audio': //音频文件
        if (!RegExp('\\w\\.(' + (exts || 'mp3|wav|mid') + ')$', 'i').test(escape(value))) {
          that.msg('选择的音频中包含不支持的格式');
          return elemFile.value = '';
        }
        break;
      default: //图片文件
        layui.each(value, function (i, item) {
          if (!RegExp('\\w\\.(' + (exts || 'jpg|png|gif|bmp|jpeg$') + ')', 'i').test(escape(item))) {
            check = true;
          }
        });
        if (check) {
          that.msg('选择的图片中包含不支持的格式');
          return elemFile.value = '';
        }
        break;
    }

    //检验文件大小
    if (options.size > 0 && !(device.ie && device.ie < 10)) {
      var limitSize;
      layui.each(that.chooseFiles, function (index, file) {
        if (file.size > 1024 * options.size) {
          var size = options.size / 1024;
          size = size >= 1
            ? (Math.floor(size) + (size % 1 > 0 ? size.toFixed(1) : 0)) + 'MB'
            : options.size + 'KB'
          elemFile.value = '';
          limitSize = size;

        }
      });
      if (limitSize) return that.msg('文件不能超过' + limitSize);
    }
    send();
  };

  //事件处理
  Class.prototype.events = function () {
    var that = this
      , options = that.config

      //设置当前选择的文件队列
      , setChooseFile = function (files) {
        that.chooseFiles = {};
        layui.each(files, function (i, item) {
          var time = new Date().getTime();
          that.chooseFiles[time + '-' + i] = item;
        });
      }

      //设置选择的文本
      , setChooseText = function (files, filename) {
        var elemFile = that.elemFile
          , value = files.length > 1
            ? files.length + '个文件'
            : ((files[0] || {}).name || (elemFile[0].value.match(/[^\/\\]+\..+/g) || []) || '');

        if (elemFile.next().hasClass(ELEM_CHOOSE)) {
          elemFile.next().remove();
        }
        that.upload(null, 'choose');
        if (that.isFile() || options.choose) return;
        elemFile.after('<span class="layui-inline ' + ELEM_CHOOSE + '">' + value + '</span>');
      };

    //点击上传容器
    options.elem.off('upload.start').on('upload.start', function () {
      var othis = $(this), data = othis.attr('lay-data');

      if (data) {
        try {
          data = new Function('return ' + data)();
          that.config = $.extend({}, options, data);
        } catch (e) {
          hint.error('Upload element property lay-data configuration item has a syntax error: ' + data)
        }
      }

      that.config.item = othis;
      that.elemFile[0].click();
    });

    //拖拽上传
    if (!(device.ie && device.ie < 10)) {
      options.elem.off('upload.over').on('upload.over', function () {
        var othis = $(this)
        othis.attr('lay-over', '');
      })
        .off('upload.leave').on('upload.leave', function () {
          var othis = $(this)
          othis.removeAttr('lay-over');
        })
        .off('upload.drop').on('upload.drop', function (e, param) {
          var othis = $(this), files = param.originalEvent.dataTransfer.files || [];

          othis.removeAttr('lay-over');
          setChooseFile(files);

          if (options.auto) {
            that.upload(files);
          } else {
            setChooseText(files);
          }
        });
    }

    //文件选择
    that.elemFile.off('upload.change').on('upload.change', function () {
      var files = this.files || [];
      setChooseFile(files);
      options.auto ? that.upload() : setChooseText(files); //是否自动触发上传
    });

    //手动触发上传
    options.bindAction.off('upload.action').on('upload.action', function () {
      that.upload();
    });

    //防止事件重复绑定
    if (options.elem.data('haveEvents')) return;

    that.elemFile.on('change', function () {
      $(this).trigger('upload.change');
    });

    options.elem.on('click', function () {
      if (that.isFile()) return;
      $(this).trigger('upload.start');
    });

    if (options.drag) {
      options.elem.on('dragover', function (e) {
        e.preventDefault();
        $(this).trigger('upload.over');
      }).on('dragleave', function (e) {
        $(this).trigger('upload.leave');
      }).on('drop', function (e) {
        e.preventDefault();
        $(this).trigger('upload.drop', e);
      });
    }

    options.bindAction.on('click', function () {
      $(this).trigger('upload.action');
    });

    options.elem.data('haveEvents', true);
  };

  //核心入口  
  upload.render = function (options) {
    var inst = new Class(options);
    return thisUpload.call(inst);
  };

  exports(MOD_NAME, upload);
});

